package com.lagou.filter;

import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.*;

import java.sql.SQLOutput;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import java.util.stream.Stream;

@Activate(group = {CommonConstants.CONSUMER})
public class TPMonitorFilter implements Filter {

    TreeMap<Long, Long> treeMapA = new TreeMap<>(Comparator.naturalOrder());
    TreeMap<Long, Long> treeMapB = new TreeMap<>(Comparator.naturalOrder());
    TreeMap<Long, Long> treeMapC = new TreeMap<>(Comparator.naturalOrder());

    Metrics metrics = new Metrics();


    public TPMonitorFilter(){

        System.out.println("==================TPMonitorFilter");
        ScheduledExecutorService scheduledExecutorService =
                Executors.newScheduledThreadPool(1);
        scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                System.out.println("执行一次统计：");
                metrics.getLast60SForA();
            }
        },5,5, TimeUnit.SECONDS);

    }
    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        String methodName = invocation.getMethodName();
        long   startTime  = System.currentTimeMillis();
        try {
            // 执行方法
            return  invoker.invoke(invocation);
        } finally {
              long cur =System.currentTimeMillis();
              long spendTime =cur-startTime;
            //获取当前秒

              if(methodName.equals("methodA")){
                    metrics.incrA(cur,spendTime);
              }else if(methodName.equals("methodB")){

              }else if(methodName.equals("methodC")){

              }

        }


    }




    static  class Metrics {
        TreeMap<Long, LinkedBlockingQueue> treeMapA = new TreeMap<>(Comparator.naturalOrder());
        AtomicReference<Long> idxA= new AtomicReference<>(0l);

        public void incrA(long cur,long spendTime){
            LinkedBlockingQueue linkedBlockingQueue = null;
            if(treeMapA.containsKey(cur)){
                linkedBlockingQueue =  treeMapA.get(cur);

            }else {
                 linkedBlockingQueue = new LinkedBlockingQueue();
                 treeMapA.put(cur,linkedBlockingQueue);
            }
            linkedBlockingQueue.offer(spendTime);
        }


        //获取最近60s内数据，并清除之前的数据
        public void getLast60SForA(){
            try {

                if(treeMapA.size() ==0) {
                    return;
                }
                TreeMap<Long, Long> treeMap = new TreeMap<>(Comparator.naturalOrder());
                //1.  获取开始时间， 当前时间 -60   = 计算开始时间
                long endTime=  System.currentTimeMillis();
                long startTime = endTime- 60 * 1000;


                //2.获取从startTime开始的数据(删除之前的元素)
                Iterator<Map.Entry<Long, LinkedBlockingQueue>> iterator = treeMapA.entrySet().iterator();
                while (iterator.hasNext()) {
                    Map.Entry<Long, LinkedBlockingQueue> iter = iterator.next();
                    if (iter.getKey() < startTime) {
                        iter.getValue().clear();
                        iterator.remove();
                    } else {
                        break;
                    }
                }
                //3.统计该段时间内每个响应时间的次数
                iterator = treeMapA.entrySet().iterator();
                Map.Entry<Long, LinkedBlockingQueue> iter = null;
                if(iterator.hasNext()) {
                    iter = iterator.next();
                }else{
                    return;
                }

                while (iter.getKey() <= endTime ) {

                    LinkedBlockingQueue blockingQueue = iter.getValue();
                    blockingQueue.forEach(new Consumer() {
                        @Override
                        public void accept(Object o) {
                            if (treeMap.containsKey(o)) {
                                treeMap.put((Long) o, treeMap.get(o) + 1);
                            } else {
                                treeMap.put((Long) o, 1l);
                            }
                        }
                    });
                    if(iterator.hasNext()) {
                        iter = iterator.next();
                    }else{
                        break;
                    }

                }


                TreeMap<Long, Long> transMap = new TreeMap<>(Comparator.naturalOrder());
                AtomicReference<Long> idx = new AtomicReference<>(0l);
                treeMap.forEach((key, value) -> {
                    transMap.put(idx.get(), key);
                    idx.updateAndGet(v -> v + value);
                });
                Long total = treeMap.values().stream().mapToLong(o -> o).sum();
                Double idx90 = Math.ceil((double) total * 90 / 100);
                Double idx99 = Math.ceil((double) total * 99 / 100);

                Map.Entry<Long, Long> idx90Entry = transMap.floorEntry(idx90.longValue());
                Map.Entry<Long, Long> idx99Entry = transMap.floorEntry(idx99.longValue());

                Long TP90 = idx90Entry.getValue();
                Long TP99 = idx99Entry.getValue();

                System.out.println("TP90:" + TP90 + ", TP99:" + TP99);
            }catch (Exception e) {
                e.printStackTrace();
            }
        }
    }


    public static void main(String[] args) throws InterruptedException {

        //TreeMap<Long, Long> treeMap = new TreeMap<>(Comparator.naturalOrder());
        // <1,1> <5,3> <6,1> <2,2> <3,3> <4,2> <7,2> <9,3> <10,3>

        Metrics metrics = new Metrics();
        Random random = new Random();
        for(int i=0;i<100;i++) {
            long cur = System.currentTimeMillis();
            int time = random.nextInt(100);
            System.out.println(time);
            metrics.incrA(cur,time );
            Thread.sleep(time);

        }

        metrics.getLast60SForA();

    }
}
